﻿using Analyse.DrawingPerformance.Model;
using SharpDX.Direct2D1;
using SharpDX.DXGI;
using SharpDX.Mathematics.Interop;
using SharpDX.WIC;
using System;
using System.Collections.Generic;
using System.IO;

namespace Analyse.DrawingPerformance.Contract
{
    public class SharpDxDrawingEngine : IDrawingEngine
    {
        private Direct2DImageEncoder direct2DImageEncoder;

        public SharpDxDrawingEngine()
        {
            direct2DImageEncoder = new Direct2DImageEncoder(1920, 1080, 96);
        }

        public byte[] Draw(int width, int height, List<BaseDrawElement> drawElements)
        {
            //var test = new Direct2DImageEncoder(width, height, 96);
            using (var memoryStream = new MemoryStream())
            {

                direct2DImageEncoder.Save(memoryStream, Direct2DImageFormat.Png);
                return memoryStream.ToArray();
            }
                

            //var functions = new Dictionary<Type, Func<SKSurface, BaseDrawElement, bool>>();
            //functions.Add(typeof(ImageDrawElement), DrawImage);
            //functions.Add(typeof(TextDrawElement), DrawText);

            //var info = new SKImageInfo(width, height);
            //using (var surface = SKSurface.Create(info))
            //{
            //    surface.Canvas.Clear(SKColor.Parse("#ffffff"));

            //    if (drawElements != null)
            //    {
            //        foreach (var drawElement in drawElements)
            //        {
            //            functions.TryGetValue(drawElement.GetType(), out var function);
            //            function?.Invoke(surface, drawElement);
            //        }
            //    }

            //    using (var image = surface.Snapshot())
            //    using (var data = image.Encode(SKEncodedImageFormat.Png, 100))
            //    {
            //        return data.ToArray();
            //    }
            //}
        }

        //private bool DrawImage(SKSurface surface, BaseDrawElement drawElement)
        //{
        //    var item = drawElement as ImageDrawElement;
        //    using (var paint = new SKPaint())
        //    {
        //        using (var bitmap = SKBitmap.Decode(item.ImagePath))
        //        {
        //            surface.Canvas.DrawBitmap(bitmap, item.X, item.Y, paint);
        //        }
        //    }

        //    return true;
        //}

        //private bool DrawText(SKSurface surface, BaseDrawElement drawElement)
        //{
        //    var item = drawElement as TextDrawElement;
        //    var color = ColorTranslator.FromHtml(item.Color);

        //    using (var paint = new SKPaint())
        //    {
        //        paint.TextSize = item.Size;
        //        paint.IsAntialias = true;
        //        paint.Color = new SKColor(color.R, color.G, color.B);
        //        paint.IsStroke = false;
        //        if (!string.IsNullOrEmpty(item.FontPath))
        //        {
        //            paint.Typeface = SKTypeface.FromFile(item.FontPath);
        //        }

        //        surface.Canvas.DrawText(item.Text, item.X, item.Y, paint);
        //    }

        //    return true;
        //}

        public class Direct2DImageEncoder : IDisposable
        {
            private readonly Direct2DFactoryManager factoryManager;
            private readonly SharpDX.WIC.Bitmap wicBitmap;
            private readonly WicRenderTarget renderTarget;

            private readonly int imageWidth, imageHeight;

            public Direct2DImageEncoder(int imageWidth, int imageHeight, int imageDpi)
            {
                this.imageWidth = imageWidth;
                this.imageHeight = imageHeight;

                factoryManager = new Direct2DFactoryManager();

                wicBitmap = new SharpDX.WIC.Bitmap(factoryManager.WicFactory, imageWidth, imageHeight, SharpDX.WIC.PixelFormat.Format32bppBGR, BitmapCreateCacheOption.CacheOnLoad);
                var renderTargetProperties = new RenderTargetProperties(RenderTargetType.Default, new SharpDX.Direct2D1.PixelFormat(Format.Unknown, SharpDX.Direct2D1.AlphaMode.Unknown), imageDpi, imageDpi, RenderTargetUsage.None, FeatureLevel.Level_DEFAULT);
                renderTarget = new WicRenderTarget(factoryManager.D2DFactory, wicBitmap, renderTargetProperties);
                renderTarget.BeginDraw();
                renderTarget.Clear(new RawColor4(255, 255, 255, 1));
                renderTarget.EndDraw();
            }

            public void Dispose()
            {
                this.wicBitmap.Dispose();
                this.renderTarget.Dispose();
                this.factoryManager.Dispose();
            }

            public void Save(Stream systemStream, Direct2DImageFormat format)
            {
                //renderTarget.EndDraw();

                using (var stream = new WICStream(factoryManager.WicFactory, systemStream))
                using (var encoder = new BitmapEncoder(factoryManager.WicFactory, Direct2DConverter.ConvertImageFormat(format)))
                {
                    encoder.Initialize(stream);

                    using (var bitmapFrameEncode = new BitmapFrameEncode(encoder))
                    {
                        bitmapFrameEncode.Initialize();
                        bitmapFrameEncode.SetSize(imageWidth, imageHeight);
                        //bitmapFrameEncode.SetPixelFormat(SharpDX.WIC.PixelFormat.FormatDontCare);
                        bitmapFrameEncode.WriteSource(wicBitmap);

                        bitmapFrameEncode.Commit();
                        encoder.Commit();
                    }
                }
            }
        }

        public class Direct2DFactoryManager : IDisposable
        {
            private readonly SharpDX.WIC.ImagingFactory wicFactory;
            private readonly SharpDX.Direct2D1.Factory d2DFactory;
            private readonly SharpDX.DirectWrite.Factory dwFactory;

            public Direct2DFactoryManager()
            {
                wicFactory = new SharpDX.WIC.ImagingFactory();
                d2DFactory = new SharpDX.Direct2D1.Factory();
                dwFactory = new SharpDX.DirectWrite.Factory();
            }
            public void Dispose()
            {
                wicFactory.Dispose();
                d2DFactory.Dispose();
                dwFactory.Dispose();
            }


            public SharpDX.WIC.ImagingFactory WicFactory
            {
                get
                {
                    return wicFactory;
                }
            }

            public SharpDX.Direct2D1.Factory D2DFactory
            {
                get
                {
                    return d2DFactory;
                }
            }

            public SharpDX.DirectWrite.Factory DwFactory
            {
                get
                {
                    return dwFactory;
                }
            }
        }

        public enum Direct2DImageFormat
        {
            Png, Gif, Ico, Jpeg, Wmp, Tiff, Bmp
        }

        public class Direct2DConverter
        {
            public static Guid ConvertImageFormat(Direct2DImageFormat format)
            {
                switch (format)
                {
                    case Direct2DImageFormat.Bmp:
                        return ContainerFormatGuids.Bmp;
                    case Direct2DImageFormat.Ico:
                        return ContainerFormatGuids.Ico;
                    case Direct2DImageFormat.Gif:
                        return ContainerFormatGuids.Gif;
                    case Direct2DImageFormat.Jpeg:
                        return ContainerFormatGuids.Jpeg;
                    case Direct2DImageFormat.Png:
                        return ContainerFormatGuids.Png;
                    case Direct2DImageFormat.Tiff:
                        return ContainerFormatGuids.Tiff;
                    case Direct2DImageFormat.Wmp:
                        return ContainerFormatGuids.Wmp;
                }
                throw new NotSupportedException();
            }
        }
    }
}
